package com.librariy.view;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.hardware.Camera;
import android.util.Log;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.widget.Toast;
import java.io.IOException;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
 * 相机包装类
 * */
@SuppressWarnings({"deprecation","StaticFieldLeak"})
public class CameraWrap{
    public static final String TAG = "CameraWrap";
    public static final int CAMERA_FACING_BACK=Camera.CameraInfo.CAMERA_FACING_BACK;
    public static final int CAMERA_FACING_FRONT=Camera.CameraInfo.CAMERA_FACING_FRONT;
    private boolean isFocusing = false;
    private boolean isPreviewing = false;
    private Camera Ins;
    private int cameraId=CAMERA_FACING_BACK;
    private int displayOrientation=0,imageOrientation=0;
    public boolean open(Context mContext, int cameraId){
        try {
            Ins=Camera.open(cameraId);
            if(Ins==null){
                Toast.makeText(mContext, "无法打开摄像头", Toast.LENGTH_LONG).show();
                return false;
            }
            this.cameraId=cameraId;
            return true;
        } catch (Exception e) {
            Toast.makeText(mContext, "无法打开摄像头", Toast.LENGTH_LONG).show();
            return false;
        }
    }
    @SuppressWarnings("deprecation")
    public void initCameraParameters(Display mDisplay) {
        if(Ins==null) return;
        Camera.Parameters p = Ins.getParameters();
        for (String mode : p.getSupportedFocusModes()) {
            if (!Camera.Parameters.FOCUS_MODE_AUTO.equalsIgnoreCase(mode)) continue;
            p.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
            Log.i(TAG, "initCameraParameters, FocusMode="+mode);
        }
        this.displayOrientation=getDisplayOrientation(mDisplay,cameraId);
        boolean mirror = (cameraId == Camera.CameraInfo.CAMERA_FACING_FRONT);
        this.imageOrientation=mirror?((displayOrientation+180)%360):displayOrientation;
        Ins.setDisplayOrientation(displayOrientation);
        p.setRotation(imageOrientation);
        Ins.setParameters(p);
    }
    public Matrix faceMapMatrix(float width,float height){
        //人脸坐标[-1000,-1000,1000,1000]变换到View[0,0,width,height]坐标
        Matrix matrix = new Matrix();
        boolean mirror = (cameraId == Camera.CameraInfo.CAMERA_FACING_FRONT);
        matrix.setScale(mirror ? -1 : 1, 1);
        matrix.postRotate(displayOrientation);
        matrix.postTranslate(1000,1000);
        matrix.postScale(width/ 2000f, height / 2000f);
        return matrix;
    }
    private static int getDisplayOrientation(Display mDisplay,int cameraId) {
          android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
          android.hardware.Camera.getCameraInfo(cameraId, info);
          int degrees=0,rotation = mDisplay.getRotation();
          switch (rotation) {
              case Surface.ROTATION_0: degrees = 0; break;
              case Surface.ROTATION_90: degrees = 90; break;
              case Surface.ROTATION_180: degrees = 180; break;
              case Surface.ROTATION_270: degrees = 270; break;
          }
          int result;
          if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
              result = (info.orientation + degrees) % 360;
              result = (360 - result) % 360;  // compensate the mirror
          } else {  // back-facing
              result = (info.orientation - degrees + 360) % 360;
          }
          return result;
    }
    public Camera.Parameters getParameters() {
        if(Ins==null) return null;
        return Ins.getParameters();
    }
    public void setParameters(Camera.Parameters parameters) {
        if(Ins==null) return;
        Ins.setParameters(parameters);
    }
    public boolean isSupporteDetectedFace() {
        if(Ins==null) return false;
        Camera.Parameters p = Ins.getParameters();
        boolean result=(p==null?false:p.getMaxNumDetectedFaces()>0);
        Log.i(TAG,"isSupporteDetectedFace="+result);
        return result;
    }
    public void autoFocus(final Camera.AutoFocusCallback autoFocusCallback) {
        try{
            this.autoFocusInternal(autoFocusCallback);
        }catch (Exception e){
            Log.e(TAG,"autoFocus Faild!",e);
        }
    }
    private void autoFocusInternal(final Camera.AutoFocusCallback autoFocusCallback) {
        if(Ins==null){
            isFocusing=false;
            return;
        }
        if(!isPreviewing){
            return;
        }
        isFocusing=true;
        Ins.autoFocus(new Camera.AutoFocusCallback(){
            @Override
            public void onAutoFocus(boolean success, Camera camera) {
                if(autoFocusCallback!=null) {
                    autoFocusCallback.onAutoFocus(success, camera);
                }
                if(success){
                    isFocusing=false;
                }
            }
        });
    }
    public void setPreviewDisplay(SurfaceHolder holder) throws IOException {
        if(Ins==null) return;
        Ins.setPreviewDisplay(holder);
    }
    public final void takePicture(boolean autoFocus,final Camera.PictureCallback jpeg) {
        if(Ins==null) return;
        if(!autoFocus){
            Ins.takePicture(null,null,jpeg);
            return;
        }
        autoFocus(new Camera.AutoFocusCallback(){
            @Override
            public void onAutoFocus(boolean success, Camera camera) {
                if(!success) return;
                Ins.takePicture(null,null,jpeg);
            }
        });
    }
    public void setFaceDetectionListener(Camera.FaceDetectionListener listener) {
        if(Ins==null) return;
        Ins.setFaceDetectionListener(listener);
    }
    public void setPreviewCallback(Camera.PreviewCallback listener) {
        if(Ins==null) return;
        Ins.setPreviewCallback(listener);
    }
    public void startPreview(boolean faceDetection) {
        if(Ins==null){
            isPreviewing=false;
        }else {
            isPreviewing = true;
            Ins.startPreview();
            if(faceDetection&&isSupporteDetectedFace()){
                Ins.startFaceDetection();
            }
        }
    }
    public void stopPreview(boolean faceDetection) {
        isPreviewing=false;
        if(Ins==null) return;
        if(faceDetection&&isSupporteDetectedFace()){
            Ins.stopFaceDetection();
        }
        Ins.stopPreview();
    }
    public void release() {
        try {
            isPreviewing = false;
            isFocusing = false;
            if (Ins == null) return;
            Ins.setPreviewCallback(null);
            Ins.setPreviewDisplay(null);
            Ins.release();
            Ins = null;
        }catch (Exception e){
            Log.e(TAG,"release()",e);
        }
    }
    public void setAdapterSize(Rect r) {
        Camera.Parameters p=Ins.getParameters();
        Camera.Size s1=getAdapterSize(p.getSupportedPictureSizes(),r.width(),r.height());
        if(s1!=null){
            p.setPictureSize(s1.width, s1.height);
        }
        Camera.Size s2=getAdapterSize(p.getSupportedPreviewSizes(),r.width(),r.height());
        if(s2!=null) {
            p.setPreviewSize(s2.width, s2.height);
        }
        Ins.setParameters(p);
    }
    private Camera.Size getAdapterSize(List<Camera.Size> list, int preWidth, int preHeight) {
        double targetRatio = (double) preHeight / preWidth;
        if (targetRatio > 1) {
            targetRatio = (double) preWidth / preHeight;
        }
        TreeMap<String,Camera.Size> optimalMap=new TreeMap<>(new Comparator<String>(){
            @Override
            public int compare(String o1, String o2) {
                return o1.compareTo(o2);
            }
        });
        for (int i=0;i<list.size();i++) {
            Camera.Size size=list.get(i);
            double ratio = (double) size.height / size.width;
            if (ratio > 1) {
                ratio = (double) size.width / size.height;
            }
            //首要规则：ratio与目标targetRatio差异越小，评分越高
            double score1=Math.abs(ratio - targetRatio);
            //次要规则：ratio相同的情况下，分辨率越高，评分越高
            double score2=1.0/(size.width*size.height);
            optimalMap.put(String.format("%.10f-%.10f", score1,score2), size);
        }
        for(String key:optimalMap.keySet()){
            Camera.Size size=optimalMap.get(key);
            Log.i(TAG,"key="+key+"; size=["+size.width+","+size.height+"]");
        }
        while(!optimalMap.isEmpty()){
            Map.Entry<String,Camera.Size> item=optimalMap.firstEntry();
            Camera.Size size=item.getValue();
            if(optimalMap.size()==1){
                Log.i(TAG,"AdapterSize => { key="+item.getKey()+"; size=["+size.width+","+size.height+"] }");
                return size;
            }
            if(size.width>=500 && size.height>=500){
                Log.i(TAG,"AdapterSize => { key="+item.getKey()+"; size=["+size.width+","+size.height+"] }");
                return size;
            }
            optimalMap.remove(item.getKey());
        }
        return null;
    }
}